package edu.hawaii.senin.iclouds.plotting;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import edu.hawaii.senin.iclouds.util.TextUtils;

/**
 *
 * Parses iClouds log and makes internal data structures that corresponds to log. Could be queried
 * for the data fond.
 *
 * @author Pavel Senin
 *
 */
public class GeneralLogParser {

  /* the population-related variables */
  private final Pattern surfacePopulationPattern = Pattern.compile("surface\\spopulation");
  private final Pattern buryPopulationPattern = Pattern.compile("buried\\spopulation");
  private final Pattern finalPopulationPattern = Pattern
      .compile("##\\sfinal\\sformatted\\sstatistics");
  private final Pattern dividerPattern = Pattern.compile("----");
  private final Pattern dividerPattern1 = Pattern.compile("##");

  private final Pattern dReleasedPattern = Pattern.compile("D_released\\,\\d+");
  private final Pattern dAccretedPattern = Pattern.compile("D_taken_from_gas\\,\\d+");

  private final Pattern hReleasedPattern = Pattern.compile("H_released\\,\\d+");
  private final Pattern hAccretedPattern = Pattern.compile("H_taken_from_gas\\,\\d+");

  private final Pattern setupPattern = Pattern.compile("current\\ssetup");

  // private final Pattern finalSectionPattern =
  // Pattern.compile("##\\sfinal\\sroutes\\sstatistics:");
  // private final Pattern routesPattern = Pattern.compile("species,");
  // private final Pattern picksPattern = Pattern.compile("picks,");
  // private TreeMap<String, Long> routesStatistics;
  // private TreeMap<String, Long> picksStatistics;

  private TreeMap<String, Long> activePopulationStatistics;
  private TreeMap<String, Long> buriedPopulationStatistics;
  private TreeMap<String, Long> totalPopulationStatistics;
  private TreeMap<String, Long> totalCoreactStatistics;
  private TreeMap<String, Long> totalReactStatistics;
  private Double density;
  private Double accretionNum;
  private Double dhRatio;
  private String auxLine;

  private StringBuffer accretedReleased;

  /**
   * Does actual parsing.
   *
   * @param fileName log file name.
   * @throws NumberFormatException if something wrong with conversion.
   * @throws IOException when IO error encountered.
   */
  public void parse(String fileName) throws NumberFormatException, IOException {

    activePopulationStatistics = new TreeMap<String, Long>();
    buriedPopulationStatistics = new TreeMap<String, Long>();
    totalPopulationStatistics = new TreeMap<String, Long>();
    totalCoreactStatistics = new TreeMap<String, Long>();
    totalReactStatistics = new TreeMap<String, Long>();

    this.accretedReleased = new StringBuffer();

    BufferedReader in = new BufferedReader(new FileReader(new File(fileName)));
    String cLine = null;

    // this cycle just takes lines from the output one by one
    while ((cLine = in.readLine()) != null) {

      /* here we are extracting specific H and D accretion and creation data */
      Matcher m = hAccretedPattern.matcher(cLine);
      if (m.find()) {
        accretedReleased.append(m.group().replace(',', '=') + ":");
      }
      m = hReleasedPattern.matcher(cLine);
      if (m.find()) {
        accretedReleased.append(m.group().replace(',', '=') + ":");
      }

      m = dAccretedPattern.matcher(cLine);
      if (m.find()) {
        accretedReleased.append(m.group().replace(',', '=') + ":");
      }
      m = dReleasedPattern.matcher(cLine);
      if (m.find()) {
        accretedReleased.append(m.group().replace(',', '=') + ":");
      }

      // this block reads active surface population statistics
      if (setupPattern.matcher(cLine).find()) {
        in.mark(1024);
        String line2 = in.readLine();
        // need to skip one line
        line2 = in.readLine();
        String[] breakedLine2 = line2.split(",");
        // extract the density
        String[] tmpBreak = TextUtils.chomp(breakedLine2[0]).split("\\s");
        this.density = Double.valueOf(tmpBreak[1]);
        // extract the accretion number
        tmpBreak = TextUtils.chomp(breakedLine2[4]).split("\\s");
        this.accretionNum = Double.valueOf(tmpBreak[2]);

        String line3 = in.readLine();
        String[] breakedLine3 = line3.split(",");
        // extract the D/H ratio
        tmpBreak = TextUtils.chomp(breakedLine3[0]).split("\\s");
        this.dhRatio = Double.valueOf(tmpBreak[4]);

      } // if setupPattern

      // this block reads active surface population statistics
      if (surfacePopulationPattern.matcher(cLine).find()) {
        in.mark(1024);
        while ((cLine = in.readLine()) != null) {
          if (dividerPattern.matcher(cLine).find()) {
            in.reset();
            break;
          }
          else {
            String[] breakedLine = cLine.split(",\\s+");
            String specieName = TextUtils.chomp(breakedLine[0]);
            Long numValue = Long.valueOf(TextUtils.chomp(breakedLine[1]));
            activePopulationStatistics.put(specieName, numValue);
            in.mark(1024);
          }
        } // while
      } // if populationPattern

      // this block reads buried surface population statistics
      if (buryPopulationPattern.matcher(cLine).find()) {
        in.mark(1024);
        while ((cLine = in.readLine()) != null) {
          if (dividerPattern.matcher(cLine).find()) {
            in.reset();
            break;
          }
          else {
            String[] breakedLine = cLine.split(",\\s+");
            String specieName = TextUtils.chomp(breakedLine[0]);
            Long numValue = Long.valueOf(TextUtils.chomp(breakedLine[1]));
            buriedPopulationStatistics.put(specieName, numValue);
            in.mark(1024);
          }
        } // while
      } // if populationPattern

      // this block reads total final surface population statistics
      if (finalPopulationPattern.matcher(cLine).find()) {
        cLine = in.readLine();
        this.auxLine = in.readLine().replace(' ', ':');
        cLine = in.readLine();

        in.mark(4096);
        while ((cLine = in.readLine()) != null) {
          if (dividerPattern.matcher(cLine).find()) {
            in.reset();
            break;
          }
          else {
            String[] breakedLine = cLine.split("\\s+");
            String specieName = TextUtils.chomp(breakedLine[1]);
            Long numValue = Long.valueOf(TextUtils.chomp(breakedLine[2]));
            totalPopulationStatistics.put(specieName, numValue);
            totalCoreactStatistics.put(specieName, Long.valueOf(TextUtils.chomp(breakedLine[4])));
            totalReactStatistics.put(specieName, Long.valueOf(TextUtils.chomp(breakedLine[5])));
          }
        } // while
      } // if populationPattern

    } // while line...

    in.close();

  }

  /**
   * Reports active grain population as the result of log parsing.
   *
   * @return active on-surface population.
   */
  public TreeMap<String, Long> getActivePopulation() {
    return this.activePopulationStatistics;
  }

  /**
   * Reports buried grain population as the result of log parsing.
   *
   * @return buried grain population.
   */
  public TreeMap<String, Long> getBuriedPopulation() {
    return this.buriedPopulationStatistics;
  }

  /**
   * Reports total (active+buried) grain population as the result of log parsing.
   *
   * @return total grain population.
   */
  public TreeMap<String, Long> getTotalPopulation() {
    return this.totalPopulationStatistics;
  }

  /**
   * Reports parsed run log density.
   *
   * @return cloud density.
   */
  public Double getDensity() {
    return this.density;
  }

  /**
   * Reports parsed accretion number.
   *
   * @return accretion events number.
   */
  public Double getAccretionN() {
    return this.accretionNum;
  }

  /**
   * Reports parsed D/H ratio.
   *
   * @return D/H ratio.
   */
  public Double getDHRatio() {
    return this.dhRatio;
  }

  /**
   * Reports coreactant picking.
   *
   * @return coreactant picking map.
   */
  public TreeMap<String, Long> getCoreactMap() {
    return this.totalCoreactStatistics;
  }

  /**
   * Reports reactant picking.
   *
   * @return reactant picking map.
   */
  public TreeMap<String, Long> getReactMap() {
    return this.totalReactStatistics;
  }

  /**
   * Reports auxilliary statistics map.
   *
   * @return auxilliary statistics map.
   */
  public String getAuxLine() {
    return this.auxLine;
  }

  public String getAccretedReleased() {
    return this.accretedReleased.toString();
  }
}
